//
//  Location.swift
//  LocationTrackerAdmin
//
//  Created by mac on 2025/2/19.
//

import Foundation
import Vapor
import Queues
import FluentKit

struct LocationInfoParam: Content {
    let longitude: Double
    let latitude: Double
    let altitude: Double?
    let accuracy: Double
    let altitudeAccuracy: Double?
    let heading: Double?
    let speed: Double?
    let detail: String
    let timestamp: Double
}

struct Location: RouteCollection {
    func boot(routes: any RoutesBuilder) throws {
        let group = routes
            .grouped("location")
        group.post("report", use: self.report)
        group.post("fetch", use: self.fetch)
        group.get("query", use: self.query)
        group.post("getOfflineLocation", use: self.getOfflineLocation(request:))
    }
    
    struct Coordinate {
        let latitude: Double
        let longitude: Double
    }

    func haversineDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {
        // 地球半径（单位：米）
        let earthRadius: Double = 6_371_000

        // 将经纬度从度数转换为弧度
        let lat1Rad = lat1 * .pi / 180
        let lon1Rad = lon1 * .pi / 180
        let lat2Rad = lat2 * .pi / 180
        let lon2Rad = lon2 * .pi / 180

        // 计算纬度和经度的差值
        let deltaLat = lat2Rad - lat1Rad
        let deltaLon = lon2Rad - lon1Rad

        // Haversine 公式
        let a = sin(deltaLat / 2) * sin(deltaLat / 2) +
                cos(lat1Rad) * cos(lat2Rad) *
                sin(deltaLon / 2) * sin(deltaLon / 2)
        let c = 2 * atan2(sqrt(a), sqrt(1 - a))

        // 计算距离
        let distance = earthRadius * c
        return distance
    }

    func isLocation(point: Coordinate, fallInArea fences: [TaskModel.FenceData]) -> Bool {
        var isInside = false
        for fence in fences {
            let type = fence.type
            let polygon = fence.locations
            let count = fence.locations.count
            if type == 0 || type == 2 {
                //矩形
                for i in 0 ..< count {
                    let j = (i + 1) % count
                    let xi = polygon[i].longitude
                    let yi = polygon[i].latitude
                    let xj = polygon[j].longitude
                    let yj = polygon[j].latitude
                    let intersect = ((yi > point.latitude) != (yj > point.latitude)) &&
                                    (point.longitude < (xj - xi) * (point.latitude - yi) / (yj - yi) + xi)
                    if intersect {
                        isInside.toggle()
                    }
                }
                if isInside {
                    return true
                }
            } else if type == 1 {
                //圆形
                if count != 2 {
                    continue
                }
                let radius = haversineDistance(lat1: polygon[0].latitude, lon1: polygon[0].longitude, lat2: polygon[1].latitude, lon2: polygon[0].longitude)
                let distance = haversineDistance(lat1: polygon[0].latitude, lon1: polygon[0].longitude, lat2: point.latitude, lon2: point.longitude)
                if distance <= radius {
                    return true
                }
            }
        }
        return false
    }
    //上报地理位置
    @Sendable
    func report(request: Request) async throws -> JSONResponse<Bool> {
        let headers = request.headers
        let deviceID = headers["did"].first ?? ""
        let locationInfo = try request.content.decode(LocationInfoParam.self)
        let user = try await UserModel.query(on: request.db)
            .filter(\.$id, .equal, deviceID)
            .first()
        let generateDate = Date(timeIntervalSince1970: TimeInterval(locationInfo.timestamp))
        let locationModel = LocationModel(
            id: UUID().uuidString,
            deviceID: deviceID,
            createDate: generateDate,
            latitude: locationInfo.latitude,
            longitude: locationInfo.longitude,
            altitude: locationInfo.altitude,
            accuracy: locationInfo.accuracy,
            altitudeAccuracy: locationInfo.altitudeAccuracy,
            heading: locationInfo.heading,
            speed: locationInfo.speed,
            detail: locationInfo.detail
        )
        user?.latitude = locationInfo.latitude
        user?.longitude = locationInfo.longitude
        user?.detail = locationInfo.detail
        user?.locationUpdateDate = Date(timeIntervalSince1970: locationInfo.timestamp)
        try await locationModel.save(on: request.db)
        try await user?.save(on: request.db)
        //查询有没有事件，某个事件有没有触发
        let events = try await TaskModel.query(on: request.db)
            .filter(\.$monitoredID, .equal, deviceID)
            .filter(\.$status, .equal, 1)
            .all()
        events.forEach { task in
            let fences = task.fenceData ?? []
            let location = Coordinate(latitude: locationInfo.latitude, longitude: locationInfo.longitude)
            let inarea = self.isLocation(point: location, fallInArea: fences)
            if (task.type == 0 && !inarea) {
                //越界
                task.status = 2//这个任务结束，发送通知
                task.updateDate = generateDate
                task.locationsWhenTaskOccured = [
                    TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                ]
                task.save(on: request.db).whenSuccess({})
                APNSModel.query(on: request.db)
                    .group(.or, { builder in
                        builder.filter(\.$deviceID == (user?.id ?? ""))
                        builder.filter(\.$deviceID == task.initiatorID)
                    })
                    .all()
                    .whenSuccess { tokens in
                        request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "越界警报", body: "\(user?.name ?? "您的监测对象")触发了越界报警，请注意安全", avatar: user?.avatar ?? "", mutableContent: false))
                            .whenSuccess { _ in
                                
                            }
                    }
            } else if (task.type == 1 && inarea) {
                //入区
                task.status = 2//这个任务已触发
                task.locationsWhenTaskOccured = [
                    TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                ]
                task.updateDate = generateDate
                task.save(on: request.db).whenSuccess({})
                APNSModel.query(on: request.db)
                    .group(.or, { builder in
                        builder.filter(\.$deviceID == (user?.id ?? ""))
                        builder.filter(\.$deviceID == task.initiatorID)
                    })
                    .all()
                    .whenSuccess { tokens in
                        request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "入区报警", body: "\(user?.name ?? "您的监测对象")触发了入区报警，请注意安全", avatar: user?.avatar ?? "", mutableContent: false))
                            .whenSuccess { _ in
                                
                            }
                    }
            } else if (task.type == 2) {
                //未达
                if let expire = task.expiryDate {
                    let now = Date()
                    if (now <= expire && inarea) {
                        task.status = 3//这个任务结束
                        task.locationsWhenTaskOccured = [
                            TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                        ]
                        task.updateDate = generateDate
                        task.save(on: request.db).whenSuccess({})
                        request.queue.clear(JobIdentifier(string: task.id!))
                            .whenSuccess({})
                        APNSModel.query(on: request.db)
                            .group(.or, { builder in
                                builder.filter(\.$deviceID == (user?.id ?? ""))
                                builder.filter(\.$deviceID == task.initiatorID)
                            })
                            .all()
                            .whenSuccess { tokens in
                                request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "已到达", body: "\(user?.name ?? "您的监测对象")已到达您指定的区域", avatar: user?.avatar ?? "", mutableContent: false))
                                    .whenSuccess { _ in
                                        
                                    }
                            }
                    }
                }
            } else if (task.type == 3) {
                //行驶安全
            } else if (task.type == 4) {
                //SOS,不在这里处理
            }
        }
        return JSONResponse(message: "", code: 0, data: true)
    }
    
    //上报地理位置
    @Sendable
    func fetch(request: Request) async throws -> JSONResponse<Bool> {
        let headers = request.headers
        let deviceID = headers["did"].first ?? ""
        let locationInfo = try request.content.decode(LocationInfoParam.self)
        do {
            let user = try await UserModel.query(on: request.db)
                .filter(\.$id, .equal, deviceID)
                .first()
            let generateDate = Date(timeIntervalSince1970: TimeInterval(locationInfo.timestamp))
            let locationModel = LocationModel(
                id: UUID().uuidString,
                deviceID: deviceID,
                createDate: generateDate,
                latitude: locationInfo.latitude,
                longitude: locationInfo.longitude,
                altitude: locationInfo.altitude,
                accuracy: locationInfo.accuracy,
                altitudeAccuracy: locationInfo.altitudeAccuracy,
                heading: locationInfo.heading,
                speed: locationInfo.speed,
                detail: locationInfo.detail
            )
            user?.latitude = locationInfo.latitude
            user?.longitude = locationInfo.longitude
            user?.detail = locationInfo.detail
            user?.locationUpdateDate = Date(timeIntervalSince1970: locationInfo.timestamp)
            try await locationModel.save(on: request.db)
            try await user?.save(on: request.db)
            
            //查询有没有事件，某个事件有没有触发
            let events = try await TaskModel.query(on: request.db)
                .filter(\.$monitoredID, .equal, deviceID)
                .filter(\.$status, .equal, 1)
                .all()
            events.forEach { task in
                let fences = task.fenceData ?? []
                let location = Coordinate(latitude: locationInfo.latitude, longitude: locationInfo.longitude)
                let inarea = self.isLocation(point: location, fallInArea: fences)
                if (task.type == 0 && !inarea) {
                    //越界
                    task.status = 2//这个任务结束，发送通知
                    task.updateDate = generateDate
                    task.locationsWhenTaskOccured = [
                        TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                    ]
                    task.save(on: request.db).whenSuccess({})
                    APNSModel.query(on: request.db)
                        .group(.or, { builder in
                            builder.filter(\.$deviceID == (user?.id ?? ""))
                            builder.filter(\.$deviceID == task.initiatorID)
                        })
                        .all()
                        .whenSuccess { tokens in
                            request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "越界警报", body: "\(user?.name ?? "您的监测对象")触发了越界报警，请注意安全", avatar: user?.avatar ?? "", mutableContent: false))
                                .whenSuccess { _ in
                                    
                                }
                        }
                } else if (task.type == 1 && inarea) {
                    //入区
                    task.status = 2//这个任务已触发
                    task.locationsWhenTaskOccured = [
                        TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                    ]
                    task.updateDate = generateDate
                    task.save(on: request.db).whenSuccess({})
                    APNSModel.query(on: request.db)
                        .group(.or, { builder in
                            builder.filter(\.$deviceID == (user?.id ?? ""))
                            builder.filter(\.$deviceID == task.initiatorID)
                        })
                        .all()
                        .whenSuccess { tokens in
                            request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "入区报警", body: "\(user?.name ?? "您的监测对象")触发了入区报警，请注意安全", avatar: user?.avatar ?? "", mutableContent: false))
                                .whenSuccess { _ in
                                    
                                }
                        }
                } else if (task.type == 2) {
                    //未达
                    if let expire = task.expiryDate {
                        let now = Date()
                        if (now <= expire && inarea) {
                            task.status = 3//这个任务结束
                            task.locationsWhenTaskOccured = [
                                TaskModel.LocationWhenTaskOccured(latitude: locationInfo.latitude, longitude: locationInfo.longitude, detail: locationInfo.detail, date: generateDate)
                            ]
                            task.updateDate = generateDate
                            task.save(on: request.db).whenSuccess({})
                            request.queue.clear(JobIdentifier(string: task.id!))
                                .whenSuccess({})
                            APNSModel.query(on: request.db)
                                .group(.or, { builder in
                                    builder.filter(\.$deviceID == (user?.id ?? ""))
                                    builder.filter(\.$deviceID == task.initiatorID)
                                })
                                .all()
                                .whenSuccess { tokens in
                                    request.queue.dispatch(PushJob.self, PushJob.Payload(receivers: tokens.map({$0.token}), eventId: task.id!, title: "已到达", body: "\(user?.name ?? "您的监测对象")已到达您指定的区域", avatar: user?.avatar ?? "", mutableContent: false))
                                        .whenSuccess { _ in
                                            
                                        }
                                }
                        }
                    }
                } else if (task.type == 3) {
                    //行驶安全
                } else if (task.type == 4) {
                    //SOS,不在这里处理
                }
            }
            //向查询人发送一条位置消息
            let requests = try await OfflineLocationFetchRequestModel.query(on: request.db)
                .filter(\.$responseUserID == deviceID)
                .filter(\.$state == 0)
                .all()
            for user in requests {
                let _ = try await request.application.imClient.sendLocationMessage(from: deviceID, to: user.requestUserID, location: locationInfo)
            }
            try await OfflineLocationFetchRequestModel.query(on: request.db)
                .filter(\.$responseUserID == deviceID)
                .filter(\.$state == 0)
                .set(\.$state, to: 1)
                .update()
            return JSONResponse(message: "", code: 0, data: true)
        } catch {
            return JSONResponse(message: "", code: -1, data: false)
        }
    }
    
    struct LocationInfo: Content {
        let longitude: Double
        let latitude: Double
        let altitude: Double?
        let accuracy: Double
        let altitudeAccuracy: Double?
        let heading: Double?
        let speed: Double?
        let date: TimeInterval
        let detail: String
    }
    //查询某个用户的地理位置数据
    @Sendable
    func query(request: Request) async throws -> JSONResponse<[LocationInfo]> {
        struct QueryParam: Content {
            let id: String
            let beginDate: TimeInterval
            let endDate: TimeInterval
        }
        let deviceID = request.headers["did"].first ?? ""
        let queryParam = try request.query.decode(QueryParam.self)
        if queryParam.beginDate > queryParam.endDate {
            throw Abort(.badRequest, reason: "beginDate must be less than endDate")
        }
        let now = Date().timeIntervalSince1970
        if queryParam.beginDate > now {
            throw Abort(.badRequest, reason: "beginDate must be less than current time")
        }
        //查看是不是会员
        let user = try await UserModel.query(on: request.db)
            .filter(\.$id == deviceID)
            .first()
        if user == nil {
            return JSONResponse(message: "", code: -3, data: nil)
        }
        let isVIP = user?.isVIP ?? false
        let sandBox = Environment.get("SanxBox") == "1"
        if isVIP == false && sandBox == false {
            return JSONResponse(message: "非会员无法查看轨迹", code: -2, data: nil)
        }
        let beginDate = Date(timeIntervalSince1970: queryParam.beginDate)
        let endDate = Date(timeIntervalSince1970: queryParam.endDate)
        let locations = try await LocationModel.query(on: request.db)
            .filter(\.$deviceID, .equal, queryParam.id)
            .group(.and, { group in
                group.filter(\.$createDate, .greaterThanOrEqual, beginDate)
                group.filter(\.$createDate, .lessThanOrEqual, endDate)
            })
            .sort(\.$createDate, .ascending)
            .all()
        
        let locationInfos = locations.map { location in
            LocationInfo(
                longitude: location.longitude ?? 0,
                latitude: location.latitude ?? 0,
                altitude: location.altitude,
                accuracy: location.accuracy ?? 0,
                altitudeAccuracy: location.altitudeAccuracy,
                heading: location.heading,
                speed: location.speed,
                date: location.createDate.timeIntervalSince1970,
                detail: location.detail ?? ""
            )
        }
        return JSONResponse(message: "Query successful", code: 0, data: locationInfos)
    }
    
    @Sendable
    func getOfflineLocation(request: Request) async throws -> JSONResponse<Bool> {
        let headers = request.headers
        let deviceID = headers["did"].first ?? ""
        struct GetOfflineLocationParam: Content {
            let id: String
        }
        let locationInfo = try request.content.decode(GetOfflineLocationParam.self)
        let user = try await UserModel.query(on: request.db)
            .filter(\.$id, .equal, deviceID)
            .first()
        let sandBox = Environment.get("SanxBox") == "1"
        if (user?.isVIP ?? false) == false && sandBox == false {
            return JSONResponse(message: "查询离线位置需要VIP权限", code: -2, data: false)
        }
        let target = try await UserModel.query(on: request.db)
            .filter(\.$id, .equal, locationInfo.id)
            .first()
        let permission = target?.locationPermissions ?? -1
        let online = target?.online ?? false
        if online == false {
            //如果在线，那么该用户会获取到最新位置
            //添加一条离线请求记录
            return JSONResponse(message: "获取成功", code: 0, data: true)
        } else {
            if permission == 3 {
                //向目标用户发送一条离线位置上报的请求
                if let token = try await APNSModel.query(on: request.db)
                    .filter(\.$deviceID == (target!.id)!)
                    .first()?.locationPushToken {
                    let response = try await request.application.apns.sendLocationPush(deviceToken: token, requester: target!.id!)
                    print(response)
                    let uuid = UUID().uuidString.replacingOccurrences(of: "-", with: "")
                    let locationRequest = OfflineLocationFetchRequestModel(id: uuid, requestUserID: deviceID, responseUserID: locationInfo.id, createdAt: Date(), state: 0)
                    try await locationRequest.save(on: request.db)
                    return JSONResponse(message: "获取成功,请稍候", code: 0, data: true)
                }
                return JSONResponse(message: "用户无法上报位置", code: -1, data: false)
            } else {
                return JSONResponse(message: "对方未开启离线位置共享", code: -1, data: false)
            }
        }
    }
    
}
